//============================================================================
// Name        : render_manager.cpp
// Author      : dfoelber
//============================================================================

// INCLUDES //
#include "render_manager.h"
#include "draw/SDL_draw.h"
#include <iostream>
//////////////

RenderManager* RenderManager::instance = NULL;

/**
* Private constructor
**/
RenderManager::RenderManager()
{
}

/**
* Deconstructor
**/
RenderManager::~RenderManager()
{
	for( int i = 0; i < (int)dirty.size(); i++ )
	{
		delete dirty[i];
		dirty[i] = NULL;
	}
	
	for( int i = 0; i < (int)customs.size(); i++ )
	{
		delete customs[i];
		customs[i] = NULL;
	}
}

/**
* Singleton accessor
* 
* @return Singleton instance
**/
RenderManager* RenderManager::GetManager()
{
	if( instance == NULL )
	{
		instance = new RenderManager();
	}
	
	return instance;
}

/**
* Loads the screen
* 
* @param screen The screen
**/
void RenderManager::LoadScreen(Screen* screen)
{
	this->screen = screen;
}

/**
* Loads the background and paints it to the screen
* 
* @param surface The background surface
**/
void RenderManager::LoadBackground(Surface* surface)
{
	background = surface;
	
	if(screen)
	{
		surface->Apply( screen->GetSurface(), 0, 0 );
		screen->Update();
	}
}

/**
* Performs a render pass
**/
void RenderManager::Render()
{
	// This render method is not fully optimized! Layers and such soon to follow...
	
	// The rendering logic:
	// Apply the background and add the dirty rectangles
	// Clear the dirty rectangles
	// Apply the object, add area to dirty rectangles
	// Update the dirty rectangles
	
	int maxSize = (int)dirty.size() + (int)objects.size() + (int)customs.size();
	int currentSize = 0;
	SDL_Rect* updateRectangles = new SDL_Rect[maxSize];
	for( int i = 0; i < (int)dirty.size(); i++ )
	{
		background->Apply( screen->GetSurface(), dirty[i]->x, 
												dirty[i]->y,
												dirty[i]->w,
												dirty[i]->h,
												dirty[i]->x,
												dirty[i]->y);
		
		AddToUpdateRectangles(updateRectangles, &currentSize, dirty[i]->x, dirty[i]->y, dirty[i]->w, dirty[i]->h);
		
		delete dirty[i];
	}
	
	dirty.clear();
	
	// objects
	for( int i = 0; i < (int)objects.size(); i++ )
	{
		SDL_Rect* rect = new SDL_Rect();
		rect->x = objects[i]->GetPath()->GetX();
		rect->y = objects[i]->GetPath()->GetY();
		rect->w = objects[i]->GetPath()->GetWidth();
		rect->h = objects[i]->GetPath()->GetHeight();
		
		if( objects[i]->GetBackground() )
		{
			objects[i]->GetBackground()->Apply( screen->GetSurface(), objects[i]->GetPath()->GetX(), objects[i]->GetPath()->GetY() );
		}
		
		objects[i]->GetSurface()->Apply( screen->GetSurface(), objects[i]->GetPath()->GetX(), objects[i]->GetPath()->GetY() );
		
		dirty.push_back(rect);
	}
	
	// custom renders
	for( int i = 0; i < (int)customs.size(); i++ )
	{
		SDL_Rect* rect = new SDL_Rect();
		rect->x = customs[i]->x - 1;
		rect->y = customs[i]->y - 1;
		rect->w = customs[i]->width + 1;
		rect->h = customs[i]->height + 1;
		
		if( rect->x < 0 ) rect->x = 0;
		if( rect->y < 0 ) rect->y = 0;
		
		switch (customs[i]->type) // kinda hacky, i know :( FIX LATER
		{
		case CustomRender::TYPE_IMAGE:
			ApplyCustomImageRender((ImageCustomRender*)customs[i]);
			break;
		case CustomRender::TYPE_FONT:
			ApplyCustomFontRender((FontCustomRender*)customs[i]);
			break;
		case CustomRender::TYPE_RECT:
			ApplyCustomRectRender((RectCustomRender*)customs[i]);
			break;
		default:
			break;
		}
		
		dirty.push_back(rect);
		
		delete customs[i];
	}
	
	customs.clear();
	
	for( int i = 0; i < (int)dirty.size(); i++ )
	{
		AddToUpdateRectangles(updateRectangles, &currentSize, dirty[i]->x, dirty[i]->y, dirty[i]->w, dirty[i]->h);
	}
	
	// The SDL update call, with all the rectangles
	SDL_UpdateRects( screen->GetSurface()->GetSurface(), currentSize, updateRectangles );
	
	delete [] updateRectangles;
}

/**
* Adds an object to render
* 
* @param object The object
**/
void RenderManager::AddObject( Object* object )
{
	objects.push_back(object);
}

/**
* Removes an object from the render list
* 
* @param object The object
**/
void RenderManager::RemoveObject( Object* object )
{
	int iteration = -1;
	for (int i = 0; i < (int)objects.size(); i++)
	{
		if (object == objects[i])
			iteration = i;
	}
	
	if (iteration > -1)
	{
		objects.erase(objects.begin() + iteration);
	}
}

/**
* Adds a custom render request, good for one render pass.
* 
* @param custom The custom render request
**/
void RenderManager::AddCustomRender( CustomRender* custom )
{
	customs.push_back(custom);
}

/**
* Helper function to (possibly) add another rectangle to update.
**/
void RenderManager::AddToUpdateRectangles(SDL_Rect* rects, int* currentSize, int x, int y, int width, int height)
{
	rects[*currentSize].x = x;
	rects[*currentSize].y = y;
	rects[*currentSize].w = width;
	rects[*currentSize].h = height;
	
	(*currentSize)++;
}

/**
* Render a custom render of the image type.
**/
void RenderManager::ApplyCustomImageRender(ImageCustomRender* render)
{
	render->surface->Apply( screen->GetSurface(), render->x, render->y );
}

/**
* Render a custom render of the font type.
**/
void RenderManager::ApplyCustomFontRender(FontCustomRender* render)
{
	Graphics::DrawFont( screen->GetSurface(), render->str, render->font, render->size, render->color, render->x, render->y, render->width, render->height, render->alignment, render->alpha );
}

/**
* Render a custom render of the rect type.
**/
void RenderManager::ApplyCustomRectRender(RectCustomRender* render)
{
	if( render->fill )
	{
		Draw_FillRect( screen->GetSurface()->GetSurface(), render->x, render->y, render->width, render->height, render->color.ToUint32() );
	}
	else
	{
		Draw_Rect( screen->GetSurface()->GetSurface(), render->x, render->y, render->width, render->height, render->color.ToUint32() );
	}
}
